//// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
//package com.linqingying.gitcode.pullrequest.ui.diff
//
//import com.intellij.collaboration.async.*
//import com.intellij.collaboration.ui.codereview.diff.DiffLineLocation
//import com.intellij.collaboration.ui.codereview.diff.viewer.controlReviewIn
//import com.intellij.collaboration.ui.codereview.editor.CodeReviewComponentInlayRenderer
//import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorGutterControlsModel
//import com.intellij.collaboration.ui.codereview.editor.CodeReviewEditorModel
//import com.intellij.collaboration.util.Hideable
//import com.intellij.collaboration.util.RefComparisonChange
//import com.intellij.collaboration.util.syncOrToggleAll
//import com.intellij.diff.DiffContext
//import com.intellij.diff.DiffExtension
//import com.intellij.diff.FrameDiffTool
//import com.intellij.diff.requests.DiffRequest
//import com.intellij.diff.tools.util.base.DiffViewerBase
//import com.intellij.diff.util.LineRange
//import com.intellij.diff.util.Range
//import com.intellij.diff.util.Side
//import com.intellij.openapi.components.Service
//import com.intellij.openapi.components.service
//import com.intellij.openapi.util.Key
//import com.intellij.util.cancelOnDispose
//import kotlinx.coroutines.CoroutineScope
//import kotlinx.coroutines.Dispatchers
//import kotlinx.coroutines.coroutineScope
//import kotlinx.coroutines.flow.*
//import com.linqingying.gitcode.pullrequest.ui.comment.GCPRCompactReviewThreadViewModel
//import com.linqingying.gitcode.pullrequest.ui.comment.GCPRReviewCommentLocation
//import com.linqingying.gitcode.pullrequest.ui.editor.*
//import com.linqingying.gitcode.util.GitCodeSettings
//import kotlin.math.max
//import kotlin.math.min
//
//internal class GCPRReviewDiffExtension : DiffExtension() {
//  override fun onViewerCreated(viewer: FrameDiffTool.DiffViewer, context: DiffContext, request: DiffRequest) {
//    val project = context.project ?: return
//
//    if (viewer !is DiffViewerBase) return
//
//    val diffVm = context.getUserData(GCPRDiffViewModel.KEY) ?: return
//    val change = request.getUserData(RefComparisonChange.KEY) ?: return
//
//    project.service<InlaysController>().installInlays(diffVm, change, viewer)
//  }
//
//  @Service(Service.Level.PROJECT)
//  private class InlaysController(private val cs: CoroutineScope) {
//
//    fun installInlays(reviewVm: GCPRDiffViewModel, change: RefComparisonChange, viewer: DiffViewerBase) {
//      val settings = GitCodeSettings.getInstance()
//      cs.launchNow(Dispatchers.Main) {
//        reviewVm.getViewModelFor(change).collectLatest { changeVm ->
//          if (changeVm == null) return@collectLatest
//
//          if (settings.isAutomaticallyMarkAsViewed) {
//            changeVm.markViewed()
//          }
//
//          coroutineScope {
//            viewer.controlReviewIn(this, { locationToLine, lineToLocations ->
//              DiffEditorModel(this, changeVm, locationToLine, lineToLocations)
//            }, GCPREditorReviewModel.KEY, { createRenderer(it) })
//          }
//        }
//      }.cancelOnDispose(viewer)
//    }
//
//    private fun CoroutineScope.createRenderer(model: GCPREditorMappedComponentModel): CodeReviewComponentInlayRenderer =
//      when (model) {
//        is GCPREditorMappedComponentModel.Thread<*> -> GCPRReviewThreadEditorInlayRenderer(this, model.vm)
//        is GCPREditorMappedComponentModel.NewComment<*> -> GCPRNewCommentEditorInlayRenderer(this, model.vm)
//      }
//  }
//}
//
//internal interface GCPREditorReviewModel : CodeReviewEditorModel<GCPREditorMappedComponentModel>,
//                                           CodeReviewEditorGutterControlsModel.WithMultilineComments {
//  companion object {
//    val KEY: Key<GCPREditorReviewModel> = Key.create("GitCode.Editor.Gutter.Review.Model")
//  }
//}
//
//private class DiffEditorModel(
//  cs: CoroutineScope,
//  private val diffVm: GCPRDiffChangeViewModel,
//  private val locationToLine: (DiffLineLocation) -> Int?,
//  private val lineToLocation: (Int) -> DiffLineLocation?
//) : GCPREditorReviewModel {
//
//  override val inlays: StateFlow<Collection<GCPREditorMappedComponentModel>> = combine(
//    diffVm.threads.mapModelsToViewModels { MappedThread(it) },
//    diffVm.newComments.mapModelsToViewModels { MappedNewComment(it) }
//  ) { threads, new ->
//    threads + new
//  }.stateInNow(cs, emptyList())
//
//  override val gutterControlsState: StateFlow<CodeReviewEditorGutterControlsModel.ControlsState?> =
//    diffVm.locationsWithDiscussions.map {
//      val lines = it.mapNotNullTo(mutableSetOf(), locationToLine)
//      GCPRReviewEditorGutterControlsState(lines, if (diffVm.canComment) transferRanges(diffVm.commentableRanges) else emptyList())
//    }.stateInNow(cs, null)
//
//  private fun transferRanges(ranges: List<Range>): List<LineRange> = ranges.mapNotNull {
//    val leftRange = getSideRange(it, Side.LEFT)
//    val rightRange = getSideRange(it, Side.RIGHT)
//    if (leftRange != null && rightRange != null) {
//      LineRange(min(leftRange.start, rightRange.start), max(leftRange.end, rightRange.end))
//    }
//    else leftRange ?: rightRange
//  }
//
//  private fun getSideRange(range: Range, side: Side): LineRange? {
//    val startLineIdx = when (side) {
//      Side.LEFT -> range.start1
//      Side.RIGHT -> range.start2
//    }
//    val start = locationToLine(side to startLineIdx) ?: return null
//    val endLineIdx = when (side) {
//      Side.LEFT -> range.end1
//      Side.RIGHT -> range.end2
//    }
//    val end = locationToLine(side to endLineIdx.dec())?.inc() ?: return null
//    return LineRange(start, end)
//  }
//
//  override fun canCreateComment(lineRange: LineRange): Boolean {
//    if (lineRange.start == lineRange.end) return true
//    val loc1 = lineToLocation(lineRange.start) ?: return false
//    val loc2 = lineToLocation(lineRange.end) ?: return false
//    return loc1.first == loc2.first
//  }
//
//  override fun requestNewComment(lineRange: LineRange) {
//    val loc1 = lineToLocation(lineRange.start) ?: return
//    val loc2 = lineToLocation(lineRange.end) ?: return
//    if (loc1.first != loc2.first) return
//    val loc = GCPRReviewCommentLocation.MultiLine(loc1.first, loc1.second, loc2.second)
//    diffVm.requestNewComment(loc, true)
//  }
//
//  override fun requestNewComment(lineIdx: Int) {
//    val loc = lineToLocation(lineIdx) ?: return
//    diffVm.requestNewComment(GCPRReviewCommentLocation.SingleLine(loc.first, loc.second), true)
//  }
//
//  override fun toggleComments(lineIdx: Int) {
//    inlays.value.asSequence().filter { it.line.value == lineIdx }.filterIsInstance<Hideable>().syncOrToggleAll()
//  }
//
//  private inner class MappedThread(vm: GCPRReviewThreadDiffViewModel)
//    : GCPREditorMappedComponentModel.Thread<GCPRCompactReviewThreadViewModel>(vm) {
//    override val isVisible: StateFlow<Boolean> = vm.isVisible.combineState(hiddenState) { visible, hidden -> visible && !hidden }
//    override val line: StateFlow<Int?> = vm.location.mapState { loc -> loc?.let { locationToLine(it) } }
//  }
//
//  private inner class MappedNewComment(vm: GCPRNewCommentDiffViewModel)
//    : GCPREditorMappedComponentModel.NewComment<GCPRReviewNewCommentEditorViewModel>(vm) {
//    private val location = vm.position.location.let { it.side to it.lineIdx }
//    override val key: Any = "NEW_${vm.position.location}"
//    override val isVisible: StateFlow<Boolean> = MutableStateFlow(true)
//    override val line: StateFlow<Int?> = MutableStateFlow(locationToLine(location))
//  }
//}
